﻿#include "uframelesshelper.h"

#include <QMouseEvent>
#include <QRubberBand>

class UFramelessHelperPrivate
{
public:
    enum CursorStyleType
    {
        CursorNormal = 0,
        CursorTopLeft,
        CursorTop,
        CursorTopRight,
        CursorLeft,
        CursorBottomLeft,
        CursorBottom,
        CursorBottomRight,
        CursorRight
    };

    UFramelessHelperPrivate(UFramelessHelper* q) : q_ptr(q)
        , m_pRubberBand(new QRubberBand(QRubberBand::Rectangle)) {}
    ~UFramelessHelperPrivate();

    CursorStyleType hitTestCursor(const QPoint &curPoint);

    void handleResize(const QPoint &gMousePos);

    void mouseMove(QMouseEvent *event);

    void mouseButtonPress(QMouseEvent *event);

    void mouseButtonRelease(QMouseEvent *event);

    void hoverMove(QMouseEvent *event);

    void leave();

private:
    Q_DECLARE_PUBLIC(UFramelessHelper)
    Q_DISABLE_COPY(UFramelessHelperPrivate)
    UFramelessHelper* const q_ptr = nullptr;

    QRubberBand *m_pRubberBand = nullptr;
    QWidget *m_pDragWidget = nullptr;
    bool m_bLeftButtonPressed = false;
    bool m_widgetResizeable = false;
    CursorStyleType m_eCursorStyleType = CursorNormal;
};

UFramelessHelperPrivate::~UFramelessHelperPrivate()
{
    if (m_pRubberBand)
        m_pRubberBand->deleteLater();
}

UFramelessHelperPrivate::CursorStyleType UFramelessHelperPrivate::hitTestCursor(const QPoint &curPoint)
{
    if (nullptr == m_pDragWidget)
        return UFramelessHelperPrivate::CursorStyleType::CursorNormal;

    int margins = 4;
    QRect currentRect = m_pDragWidget->frameGeometry().adjusted(m_pDragWidget->contentsMargins().left()/2, m_pDragWidget->contentsMargins().top()/2,
                                              -m_pDragWidget->contentsMargins().right()/2, -m_pDragWidget->contentsMargins().bottom()/2);

    UFramelessHelperPrivate::CursorStyleType nRes = CursorNormal;
    m_pDragWidget->setCursor(Qt::ArrowCursor);
    if (!currentRect.adjusted(margins, margins, -margins, -margins).contains(curPoint)) {
        QRect leftRect(currentRect.x() - margins, currentRect.y() + margins, 2*margins, currentRect.height() - 2*margins);
        QRect topLeftRect(currentRect.x() - margins, currentRect.y() - margins, 2*margins, 2*margins);
        QRect bottomLeftRect(currentRect.x() - margins, currentRect.y() + currentRect.height() - margins, 2*margins, 2*margins);

        QRect RightRect(currentRect.x() + currentRect.width() - margins, currentRect.y() + margins, 2*margins, currentRect.height() - 2*margins);
        QRect TopRightRect(currentRect.x() + currentRect.width() - margins, currentRect.y() - margins, 2*margins, 2*margins);
        QRect BottomRightRect(currentRect.x() + currentRect.width() - margins, currentRect.y() + currentRect.height() - margins, 2*margins, 2*margins);

        QRect topRect(currentRect.x() + margins, currentRect.y() - margins, currentRect.width() - 2*margins, 2*margins);
        QRect BottomRect(currentRect.x() + margins, currentRect.y() + currentRect.height() - margins, currentRect.width() - 2*margins, 2*margins);
        QVector<QRect> list = {topLeftRect, topRect, TopRightRect, leftRect, bottomLeftRect, BottomRect, BottomRightRect, RightRect};

        auto iter = std::find_if(list.begin(), list.end(), [curPoint](const QRect &rect){
            return rect.contains(curPoint);
        });
        if (iter != list.end()) {
            QVector<Qt::CursorShape> cursorShapeList = {Qt::SizeFDiagCursor, Qt::SizeVerCursor, Qt::SizeBDiagCursor, Qt::SizeHorCursor,
                                                        Qt::SizeBDiagCursor, Qt::SizeVerCursor, Qt::SizeFDiagCursor, Qt::SizeHorCursor};
            QRect findRect = *iter;
            int index = list.indexOf(findRect);
            nRes = UFramelessHelperPrivate::CursorStyleType(index + 1);
            m_pDragWidget->setCursor(cursorShapeList[index]);
        }
    }

    return nRes;
}

void UFramelessHelperPrivate::handleResize(const QPoint &gMousePos)
{
    QRect origRect= m_pRubberBand->frameGeometry();

    int left = origRect.left();
    int top = origRect.top();
    int right = origRect.right();
    int bottom = origRect.bottom();
    origRect.getCoords(&left, &top, &right, &bottom);

    int minWidth = m_pDragWidget->minimumWidth();
    int minHeight = m_pDragWidget->minimumHeight();
    int m_nShadowWidth = 0;
    switch (m_eCursorStyleType)
    {
    case CursorTopLeft:
        left = gMousePos.x() - m_nShadowWidth;
        top = gMousePos.y() - m_nShadowWidth;
        break;
    case CursorTop:
        top = gMousePos.y() - m_nShadowWidth;
        break;
    case CursorTopRight:
        right = gMousePos.x() + m_nShadowWidth;
        top = gMousePos.y() - m_nShadowWidth;
        break;
    case CursorLeft:
        left = gMousePos.x() - m_nShadowWidth;
        break;
    case CursorBottomLeft:
        left = gMousePos.x() - m_nShadowWidth;
        bottom = gMousePos.y() + m_nShadowWidth;
        break;
    case CursorBottom:
        bottom = gMousePos.y() + m_nShadowWidth;
        break;
    case CursorBottomRight:
        right = gMousePos.x() + m_nShadowWidth;
        bottom = gMousePos.y() + m_nShadowWidth;
        break;
    case CursorRight:
        right = gMousePos.x() + m_nShadowWidth;
        break;
    default:
        break;
    }

    QRect newRect(QPoint(left, top), QPoint(right, bottom));

    if (newRect.isValid()) {
        if (minWidth > newRect.width()) {
            if (left != origRect.left())
                newRect.setLeft(origRect.left());
            else
                newRect.setRight(origRect.right());
        }
        if (minHeight > newRect.height()) {
            if (top != origRect.top())
                newRect.setTop(origRect.top());
            else
                newRect.setBottom(origRect.bottom());
        }

        m_pRubberBand->setGeometry(newRect);
    }
}

void UFramelessHelperPrivate::mouseMove(QMouseEvent *event)
{
    if (Qt::WindowMaximized == m_pDragWidget->windowState())
        return;

    if (m_bLeftButtonPressed && m_widgetResizeable) {
        handleResize(event->globalPos());
    }
}

void UFramelessHelperPrivate::mouseButtonPress(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        m_bLeftButtonPressed = true;
    }

    if (CursorNormal != m_eCursorStyleType)
    {
        QMetaObject::invokeMethod(m_pDragWidget, "setTitleBarMoveable", Q_ARG(bool, false));
        m_widgetResizeable = true;
        m_pRubberBand->setGeometry(m_pDragWidget->frameGeometry());
        m_pRubberBand->show();
    }
}

void UFramelessHelperPrivate::mouseButtonRelease(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        if (m_widgetResizeable && !m_pRubberBand->isHidden()) {
            QMetaObject::invokeMethod(m_pDragWidget, "setTitleBarMoveable", Q_ARG(bool, true));
            m_pRubberBand->hide();
            m_pDragWidget->setGeometry(m_pRubberBand->geometry());
        }
        m_bLeftButtonPressed = false;
        m_widgetResizeable = false;
    }
}

void UFramelessHelperPrivate::hoverMove(QMouseEvent *event)
{
    if (!m_bLeftButtonPressed) {
       m_eCursorStyleType = hitTestCursor(m_pDragWidget->mapToGlobal(event->pos()));
    }
}

void UFramelessHelperPrivate::leave()
{
    if (!m_bLeftButtonPressed) {
       m_pDragWidget->unsetCursor();
    }
}

UFramelessHelper::UFramelessHelper(QObject *parent)
    : QObject(parent)
    , d_ptr(new UFramelessHelperPrivate(this))
{
}

UFramelessHelper::~UFramelessHelper()
{
}

void UFramelessHelper::setDragWidget(QWidget *widget)
{
    Q_D(UFramelessHelper);
    if (nullptr != widget) {
        d->m_pDragWidget = widget;
        widget->setMouseTracking(true);
        widget->setAttribute(Qt::WA_Hover, true);
        widget->installEventFilter(this);
    }
}

void UFramelessHelper::removeDragWidget()
{
    Q_D(UFramelessHelper);
    if (nullptr != d->m_pDragWidget) {
        d->m_pDragWidget->setMouseTracking(false);
        d->m_pDragWidget->setAttribute(Qt::WA_Hover, false);
        d->m_pDragWidget->removeEventFilter(this);
    }
}

bool UFramelessHelper::eventFilter(QObject *watched, QEvent *event)
{
    Q_D(UFramelessHelper);
    if (nullptr == d->m_pDragWidget)
        return QObject::eventFilter(watched, event);

    if (watched != d->m_pDragWidget)
        return QObject::eventFilter(watched, event);

    switch (event->type()) {
    case QEvent::MouseMove:{
        d->mouseMove(static_cast<QMouseEvent*>(event));
        return true;
    }
    case QEvent::HoverMove:{
        d->hoverMove(static_cast<QMouseEvent*>(event));
        return true;
    }
    case QEvent::MouseButtonPress:{
        d->mouseButtonPress(static_cast<QMouseEvent*>(event));
        return true;
    }
    case QEvent::MouseButtonRelease: {
        d->mouseButtonRelease(static_cast<QMouseEvent*>(event));
        return true;
    }
    case QEvent::Leave: {
        d->leave();
        return true;
    }
    default:
        return QObject::eventFilter(watched, event);
    }
}
